/*
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
package ai.individual;

//import java.util.logging.Logger;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import gnu.trove.TIntObjectHashMap;

import java.util.List;
import java.util.concurrent.ScheduledFuture;

import javolution.util.FastList;

import com.l2jserver.Config;
import com.l2jserver.gameserver.GeoData;
import com.l2jserver.gameserver.ThreadPoolManager;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.datatables.NpcTable;
import com.l2jserver.gameserver.datatables.SkillTable;
import com.l2jserver.gameserver.datatables.SpawnTable;
import com.l2jserver.gameserver.network.serverpackets.SocialAction;
import com.l2jserver.gameserver.instancemanager.GrandBossManager;
import com.l2jserver.gameserver.model.L2CharPosition;
import com.l2jserver.gameserver.model.L2Skill;
import com.l2jserver.gameserver.model.L2Spawn;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.zone.type.L2BossZone;
import com.l2jserver.gameserver.network.serverpackets.Earthquake;
import com.l2jserver.gameserver.network.serverpackets.MoveToPawn;
import com.l2jserver.gameserver.network.serverpackets.PlaySound;
import com.l2jserver.gameserver.network.serverpackets.SpecialCamera;
import com.l2jserver.gameserver.templates.StatsSet;
import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
import ai.group_template.L2AttackableAIScript;

/**
 * Antharas AI
 * @author Emperorc
 */
public class Antharas extends L2AttackableAIScript
{
	//private static Logger _log = Logger.getLogger(Antharas.class.getName());
	private L2Character _target;
	private L2Skill _skill;
	
	private static final int ANTHARAS = 29019;

	//Antharas Status Tracking :
	private static final byte DORMANT = 0;     	//Antharas is spawned and no one has entered yet. Entry is unlocked
	private static final byte WAITING = 1;     	//Antharas is spawend and someone has entered, triggering a 30 minute window for additional people to enter
	                							//before he unleashes his attack. Entry is unlocked
	private static final byte FIGHTING = 2;    	//Antharas is engaged in battle, annihilating his foes. Entry is locked
	private static final byte DEAD = 3;        	//Antharas has been killed. Entry is locked
    
	private static final int FWA_ACTIVITYTIMEOFANTHARAS = 120;
	private static final int FWA_MAXMOBS = 10; // this includes Antharas itself
	private static final int FWA_INTERVALOFMOBSSTRONG = 120000;
	private static final int FWA_PERCENTOFBEHEMOTH = 60;
	private static final int FWA_SELFDESTRUCTTIME = 15000;
	
	// Spawn data of monsters.
	protected TIntObjectHashMap<L2Spawn> _monsterSpawn = new TIntObjectHashMap<L2Spawn>();
	protected L2GrandBossInstance _antharas = null;
	// Instance of monsters.
	protected List<L2Npc> _monsters = new FastList<L2Npc>();
	protected ScheduledFuture<?> _monsterSpawnTask = null;
	protected ScheduledFuture<?> _mobsSpawnTask = null;
	protected ScheduledFuture<?> _selfDestructionTask = null;
	
	private static long _LastAction = 0;
	
	private static L2BossZone _Zone;
	
	// Boss: Antharas
	public Antharas(int id,String name,String descr)
	{
        super(id,name,descr);
        int[] mob = {ANTHARAS,29069,29070,29071,29072,29073,29074,29075,29076};
        this.registerMobs(mob);
        _Zone = GrandBossManager.getInstance().getZone(179700,113800,-7709);
        StatsSet info = GrandBossManager.getInstance().getStatsSet(ANTHARAS);
        int status = GrandBossManager.getInstance().getBossStatus(ANTHARAS);
        if (status == DEAD)
        {
            // load the unlock date and time for antharas from DB
            long temp = (info.getLong("respawn_time") - System.currentTimeMillis());
            // if antharas is locked until a certain time, mark it so and start the unlock timer
            // the unlock time has not yet expired.  Mark Antharas as currently locked.  Setup a timer
            // to fire at the correct time (calculate the time between now and the unlock time,
            // setup a timer to fire after that many msec)
            if (temp > 0)
            {
                this.startQuestTimer("antharas_unlock", temp, null, null);
            }
            else
            {
                // the time has already expired while the server was offline. Immediately spawn antharas in his cave.
                // also, the status needs to be changed to DORMANT
                _antharas = (L2GrandBossInstance) addSpawn(ANTHARAS,185708,114298,-8221,32768,false,0);
                GrandBossManager.getInstance().setBossStatus(ANTHARAS,DORMANT);
                _antharas.broadcastPacket(new Earthquake(185708,114298,-8221,20,10));
                GrandBossManager.getInstance().addBoss(_antharas);
            }
        }
        else
        {
            int loc_x = info.getInteger("loc_x");
            int loc_y = info.getInteger("loc_y");
            int loc_z = info.getInteger("loc_z");
            int heading = info.getInteger("heading");
            int hp = info.getInteger("currentHP");
            int mp = info.getInteger("currentMP");
            _antharas = (L2GrandBossInstance) addSpawn(ANTHARAS,loc_x,loc_y,loc_z,heading,false,0);
            GrandBossManager.getInstance().addBoss(_antharas);
            _antharas.setCurrentHpMp(hp,mp);
            if (status == WAITING)
            {
                // Start timer to lock entry after 30 minutes
                this.startQuestTimer("waiting",1800000, _antharas, null);
            }
            else if (status == FIGHTING)
            {
                _LastAction = System.currentTimeMillis();
                // Start repeating timer to check for inactivity
                this.startQuestTimer("antharas_despawn",60000, _antharas, null, true);
            }
        }
	}

	public String onAdvEvent (String event, L2Npc npc, L2PcInstance player)
	{
        if (npc != null)
        {
        	long temp = 0;
			if (event.equalsIgnoreCase("waiting"))
            {
                npc.teleToLocation(185452,114835,-8221);
                npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(181100,114835,-7678,0));
                this.startQuestTimer("antharas_has_arrived",2000, npc, null, true);
                npc.broadcastPacket(new PlaySound(1, "BS02_A", 1, npc.getObjectId(), 185452, 114835, -8221));
                GrandBossManager.getInstance().setBossStatus(ANTHARAS,FIGHTING);
            }
            else if (event.equalsIgnoreCase("camera_1"))
            {
                this.startQuestTimer("camera_2",3000, npc, null);
                npc.broadcastPacket(new SpecialCamera(npc.getObjectId(),1000,13,20,0,20000));
            }
            else if (event.equalsIgnoreCase("camera_2"))
            {
                this.startQuestTimer("camera_3",10000, npc, null);
                npc.broadcastPacket(new SpecialCamera(npc.getObjectId(),1000,13,0,6000,20000));
            }
            else if (event.equalsIgnoreCase("camera_3"))
            {
            	npc.broadcastPacket(new SocialAction(npc.getObjectId(),2));
                this.startQuestTimer("camera_4",200, npc, null);
                npc.broadcastPacket(new SpecialCamera(npc.getObjectId(),4000,0,-3,0,10000));
            }
            else if (event.equalsIgnoreCase("camera_4"))
            {
                this.startQuestTimer("camera_5",10800, npc, null);
                npc.broadcastPacket(new SpecialCamera(npc.getObjectId(),1400,0,-3,22000,30000));
            }
            else if (event.equalsIgnoreCase("camera_5"))
            {
            	startQuestTimer("skill_range", 500, npc, null, true);
                this.startQuestTimer("antharas_despawn",60000, npc, null, true);
                npc.broadcastPacket(new SpecialCamera(npc.getObjectId(),1400,0,-3,300,3000));
                _LastAction = System.currentTimeMillis();
            }
            else if (event.equalsIgnoreCase("antharas_despawn"))
            {
                temp = (System.currentTimeMillis() - _LastAction);
                if (temp > 900000)
                {
                    npc.teleToLocation(185708,114298,-8221);
                    GrandBossManager.getInstance().setBossStatus(ANTHARAS,DORMANT);
                    npc.setCurrentHpMp(npc.getMaxHp(),npc.getMaxMp());
                    _Zone.oustAllPlayers();
                    this.cancelQuestTimer("antharas_despawn", npc, null);
                }
            }
            else if (event.equalsIgnoreCase("antharas_has_arrived"))
            {
               int dx = Math.abs(npc.getX() - 181100);
               int dy = Math.abs(npc.getY() - 114835);
               if (dx <= 50 && dy <= 50)
               {
            	   npc.broadcastPacket(new SocialAction(npc.getObjectId(),1));
                   this.startQuestTimer("camera_1",2000, npc, null);
                   npc.getSpawn().setLocx(181100);
                   npc.getSpawn().setLocy(114835);
                   npc.getSpawn().setLocz(-7678);
                   npc.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                   this.cancelQuestTimer("antharas_has_arrived", npc, null);
               }
               else
               {
                   npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO,new L2CharPosition(181100,114835,-7678,0));
               }
            }
            else if (event.equalsIgnoreCase("spawn_cubes"))
            {
                addSpawn(31859,177615,114941,-7709,0,false,900000);
                int radius = 1500;
                for (int i=0; i < 20;i++)
                {
                    int x = (int) (radius*Math.cos(i*.331)); //.331~2pi/19
                    int y = (int) (radius*Math.sin(i*.331));
                    addSpawn(31859,177615+x,114941+y,-7709,0,false,900000);
                }
                this.cancelQuestTimer("antharas_despawn", npc, null);
                this.startQuestTimer("remove_players",900000, null, null);
            }
            else if (event.equalsIgnoreCase("skill_range") && npc != null)
            {
            	callSkillAI(npc);
            }
        }
        else
        {
            if (event.equalsIgnoreCase("antharas_unlock"))
            {
                _antharas = (L2GrandBossInstance) addSpawn(ANTHARAS,185708,114298,-8221,32768,false,0);
                GrandBossManager.getInstance().addBoss(_antharas);
                GrandBossManager.getInstance().setBossStatus(ANTHARAS,DORMANT);
                _antharas.broadcastPacket(new Earthquake(185708,114298,-8221,20,10));
            }
            else if (event.equalsIgnoreCase("set_unspawn"))
            {
            	setUnspawn();
            }
        }
        return super.onAdvEvent(event, npc, player);
	}

	public String onAttack (L2Npc npc, L2PcInstance attacker, int damage, boolean isPet)
	{	
		if(npc.getNpcId() == ANTHARAS)
		{
			_LastAction = System.currentTimeMillis();
			if (GrandBossManager.getInstance().getBossStatus(ANTHARAS) != FIGHTING)
			{
				_Zone.oustAllPlayers();
			}
			else if (_mobsSpawnTask == null)
			{
				startMinionSpawns();
			}
		}
		else if (npc.getNpcId() > 29069 && npc.getNpcId() < 29077 && npc.getCurrentHp() <= damage)
		{
			L2Skill skill = null;
			switch (npc.getNpcId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
					skill = SkillTable.getInstance().getInfo(5097, 1);
					break;
				case 29076:
					skill = SkillTable.getInstance().getInfo(5094, 1);
					break;
			}

			npc.doCast(skill);
		}
        callSkillAI(npc);
        return super.onAttack(npc, attacker, damage, isPet);
	}

    public String onKill (L2Npc npc, L2PcInstance killer, boolean isPet) 
    { 
    	if(npc.getNpcId() == ANTHARAS)
    	{
    		npc.broadcastPacket(new PlaySound(1, "BS01_D", 1, npc.getObjectId(), npc.getX(), npc.getY(), npc.getZ()));
    		this.startQuestTimer("spawn_cubes", 10000, npc, null);
    		this.startQuestTimer("set_unspawn", 1800000, null, null);
    		GrandBossManager.getInstance().setBossStatus(ANTHARAS,DEAD);
    		long respawnTime = (long)Config.Interval_Of_Antharas_Spawn + Rnd.get(Config.Random_Of_Antharas_Spawn);
    		this.startQuestTimer("antharas_unlock", respawnTime, null, null);
    		// also save the respawn time so that the info is maintained past reboots
    		StatsSet info = GrandBossManager.getInstance().getStatsSet(ANTHARAS);
    		info.set("respawn_time",(System.currentTimeMillis() + respawnTime));
    		GrandBossManager.getInstance().setStatsSet(ANTHARAS,info);
    		if (getQuestTimer("skill_range", npc, null) != null)
    			getQuestTimer("skill_range", npc, null).cancel();
    	}
    	else if (npc.getNpcId() == 29069)
    	{
    		int countHPHerb = Rnd.get(6, 18);
    		int countMPHerb = Rnd.get(6, 18);
    		for (int i = 0; i < countHPHerb; i++)
    			((L2MonsterInstance)npc).dropItem(killer, 8602, 1);
    		for (int i = 0; i < countMPHerb; i++)
    			((L2MonsterInstance)npc).dropItem(killer, 8605, 1);
    	}
		if (_monsters.contains(npc))
			_monsters.remove(npc);
        return super.onKill(npc,killer,isPet);
    }
    
    public synchronized void callSkillAI(L2Npc npc)
	{
		if (npc.isInvul() || npc.isCastingNow()) return;

		if (_target == null || _target.isDead() || !(_Zone.isInsideZone(_target)))
		{
			_target = (L2Character) npc.getTarget();
			if (_target != null)
				_skill = SkillTable.getInstance().getInfo(getRandomSkill(npc),1);
		}

		L2Character target = _target;
		L2Skill skill = _skill;
		if (skill == null)
			skill = SkillTable.getInstance().getInfo(getRandomSkill(npc),1);
		if (target == null || target.isDead() || !(_Zone.isInsideZone(target)))
		{
			npc.setIsCastingNow(false);
			return;
		}

		if (Util.checkIfInRange(skill.getCastRange(), npc, target, true))
		{
			npc.getAI().setIntention(AI_INTENTION_IDLE);
			npc.setTarget(target);
			npc.setIsCastingNow(true);
			_target = null;
			_skill = null;
			if (getDist(skill.getCastRange()) > 0)
				npc.broadcastPacket(new MoveToPawn(npc,target,getDist(skill.getCastRange())));
			try
			{
				Thread.sleep(1000);
				npc.stopMove(null);
				npc.doCast(skill);
			}
			catch (Exception e)
			{e.printStackTrace();}
		}
		else
		{
			npc.getAI().setIntention(AI_INTENTION_FOLLOW, target, null);
			npc.setIsCastingNow(false);
		}
	}

	public int getRandomSkill(L2Npc npc)
	{
		/*
		 * 4106: Stun Front Aura
		 * 4107: Stun Behind Multiface
		 * 4108: Fear Area
		 * 4109: Curse Multiface
		 * 4110: Breath (Strusciata a terra) Multiface
		 * 4111: Fossilization Area
		 * 4112-13 Zampe
		 * 5092: Fear Area
		 * 5093: Stalattiti Area
		 */
		int skill;
		if( npc.getCurrentHp() > ( ( npc.getMaxHp() * 3 ) / 4 ) )
		{
			if( Rnd.get(100) < 10 )
				skill = 4110;
			else if( Rnd.get(100) < 15 )
			{
				if ( Rnd.get(10) > 3)
					skill = 4106;
				else
					skill = 4107;
			}
			else if( Rnd.get(100) < 10 )
				skill = 4108;
			else if( Rnd.get(100) < 10 )
				skill = 4109;
			else if( Rnd.get(100) < 10 )
				skill = 4111;
			else if ( Rnd.get(10) > 4)
				skill = 4112;
			else
				skill = 4113;
		}
		else if( npc.getCurrentHp() > ( ( npc.getMaxHp() * 2 ) / 4) )
		{
			if( Rnd.get(100) < 15 )
			{
				if ( Rnd.get(10) > 3)
					skill = 4106;
				else
					skill = 4107;
			}
			else if( Rnd.get(100) < 10 )
				skill = 4110;
			else if( Rnd.get(100) < 10 )
				skill = 4109;
			else if( Rnd.get(100) < 10 )
				skill = 4108;
			else if( Rnd.get(100) < 10 )
				skill = 4111;
			else if ( Rnd.get(10) > 4)
				skill = 4112;
			else
				skill = 4113;
		}
		else if( npc.getCurrentHp() > ( ( npc.getMaxHp() * 1 ) / 4 ) )
		{
			if( Rnd.get(100) < 15 )
			{
				if ( Rnd.get(10) > 4)
					skill = 4106;
				else
					skill = 5093;
			}
			else if( Rnd.get(100) < 10 )
				skill = 4109;
			else if( Rnd.get(100) < 10 )
				skill = 4110;
			else if( Rnd.get(100) < 10 )
				skill = 4108;
			else if( Rnd.get(100) < 10 )
				skill = 4111;
			else if ( Rnd.get(10) > 4)
				skill = 4112;
			else
				skill = 4113;
		}
		if( Rnd.get(100) < 10 )
			skill = 5093;
		else if( Rnd.get(100) < 15 )
		{
			if ( Rnd.get(10) > 3)
				skill = 4106;
			else
				skill = 4107;
		}
		else if( Rnd.get(100) < 10 )
			skill = 4109;
		else if( Rnd.get(100) < 10 )
			skill = 4108;
		else if( Rnd.get(100) < 10 )
			skill = 4110;	
		else if( Rnd.get(100) < 10 )
			skill = 4111;
		else if ( Rnd.get(10) > 4)
			skill = 4112;
		else
			skill = 4113;
		return skill;
	}
    
    public int getDist(int range)
	{
		int dist = 0;
		switch(range)
		{
			case -1:
				break;
			case 100:
				dist = 85;
				break;
			default:
				dist = range-85;
				break;
		}
		return dist;
	}
    
    private void startMinionSpawns()
	{
		int intervalOfMobs;

		// Interval of minions is decided by the type of Antharas
		// that invaded the lair.
	
		intervalOfMobs = FWA_INTERVALOFMOBSSTRONG;	

		// Spawn mobs.
		_mobsSpawnTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new MobsSpawn(), intervalOfMobs, intervalOfMobs);
	}

    // Do spawn Behemoth or Bomber.
	private class MobsSpawn implements Runnable
	{
		public MobsSpawn()
		{
		}

		public void run()
		{
			L2NpcTemplate template1;
			L2Spawn tempSpawn;
			boolean isBehemoth = Rnd.get(100) < FWA_PERCENTOFBEHEMOTH;
			try
			{
				int mobNumber = (isBehemoth ? 2 : 3);
				// Set spawn.
				for(int i = 0; i < mobNumber; i++)
				{
					if (_monsters.size() >= FWA_MAXMOBS)
						break;
					int npcId;
					if (isBehemoth)
						npcId = 29069;
					else
						npcId = Rnd.get(29070, 29076);
					template1 = NpcTable.getInstance().getTemplate(npcId);
					tempSpawn = new L2Spawn(template1);
					// allocates it at random in the lair of Antharas.
					int tried = 0;
					boolean notFound = true;
					int x = 175000;
					int y = 112400;
					int dt = (_antharas.getX() - x) * (_antharas.getX() - x) + (_antharas.getY() - y) * (_antharas.getY() - y);
					while (tried++ < 25 && notFound)
					{
						int rx = Rnd.get(175000, 179900);
						int ry = Rnd.get(112400, 116000);
						int rdt = (_antharas.getX() - rx) * (_antharas.getX() - rx) + (_antharas.getY() - ry) * (_antharas.getY() - ry);
						if (GeoData.getInstance().canSeeTarget(_antharas.getX(), _antharas.getY(), -7704, rx, ry, -7704))
							if (rdt < dt)
							{
								x = rx;
								y = ry;
								dt = rdt;
								if (rdt <= 900000)
									notFound = false;
							}
					}
					tempSpawn.setLocx(x);
					tempSpawn.setLocy(y);
					tempSpawn.setLocz(-7704);
					tempSpawn.setHeading(0);
					tempSpawn.setAmount(1);
					tempSpawn.setRespawnDelay(FWA_ACTIVITYTIMEOFANTHARAS * 2);
					SpawnTable.getInstance().addNewSpawn(tempSpawn, false);
					// Do spawn.
					_monsters.add(tempSpawn.doSpawn());
				}
			}
			catch (Exception e)
			{
				_log.warning(e.getMessage());
			}
		}
	}
	
	public String onAggroRangeEnter (L2Npc npc, L2PcInstance player, boolean isPet) 
	{
		switch (npc.getNpcId())
		{
			case 29070:
			case 29071:
			case 29072:
			case 29073:
			case 29074:
			case 29075:
			case 29076:
				if (_selfDestructionTask == null && !npc.isDead())
					_selfDestructionTask = ThreadPoolManager.getInstance().scheduleGeneral(new SelfDestructionOfBomber(npc), FWA_SELFDESTRUCTTIME);
				break;
		}
		return super.onAggroRangeEnter(npc, player, isPet);
	}

	// Do self destruction.
	private class SelfDestructionOfBomber implements Runnable
	{
		private L2Npc _bomber;

		public SelfDestructionOfBomber(L2Npc bomber)
		{
			_bomber = bomber;
		}

		public void run()
		{
			L2Skill skill = null;
			switch (_bomber.getNpcId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
					skill = SkillTable.getInstance().getInfo(5097, 1);
					break;
				case 29076:
					skill = SkillTable.getInstance().getInfo(5094, 1);
					break;
			}

			_bomber.doCast(skill);
			_selfDestructionTask.cancel(false);
			_selfDestructionTask = null;
		}
	}
	
	public String onSpellFinished(L2Npc npc, L2PcInstance player, L2Skill skill)
	{
		if (npc.isInvul())
		{
			return null;
		}
		else if (npc.getNpcId() == ANTHARAS)
		{
			callSkillAI(npc);
		}
		else if (skill != null && (skill.getId() == 5097 || skill.getId() == 5094))
		{
			switch (npc.getNpcId())
			{
				case 29070:
				case 29071:
				case 29072:
				case 29073:
				case 29074:
				case 29075:
				case 29076:
					npc.doDie(npc);
					break;
			}
		}
		return super.onSpellFinished(npc, player, skill);
	}
	// Clean Antharas's lair.
	public void setUnspawn()
	{
		// Eliminate players.
		_Zone.oustAllPlayers();

		if (_monsterSpawnTask != null)
		{
			_monsterSpawnTask.cancel(true);
			_monsterSpawnTask = null;
		}
		if (_mobsSpawnTask != null)
		{
			_mobsSpawnTask.cancel(true);
			_mobsSpawnTask = null;
		}
		if (_selfDestructionTask != null)
		{
			_selfDestructionTask.cancel(true);
			_selfDestructionTask = null;
		}

		// Delete monsters.
		for (L2Npc mob : _monsters)
		{
			mob.getSpawn().stopRespawn();
			mob.deleteMe();
		}
		_monsters.clear();

		// Delete teleport cube.
		/*for (L2Npc cube : _teleportCube)
		{
			cube.getSpawn().stopRespawn();
			cube.deleteMe();
		}
		_teleportCube.clear();*/
	}
	
    public static void main(String[] args)
    {
    	// now call the constructor (starts up the ai)
    	new Antharas(-1,"antharas","ai");
    }
}